package org.pearl.boot.redis.serializer;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

/**
 * Created by TD on 2021/5/14
 * ProtoStuff序列化
 */
@Slf4j
public class ProtoStuffRedisSerializer<T> implements RedisSerializer<T> {

    // RuntimeSchema是一个包含业务对象所有信息的类，包括类信息、字段信息
    private static final Schema<ProtoStuffWrapper> schema = RuntimeSchema.getSchema(ProtoStuffWrapper.class);

    /**
     * 序列化：对象=》字节数组
     *
     * @param t 需要序列化的对象t
     * @return 二进制
     * @throws SerializationException 序列化异常
     */
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return null;
        }
        // 开辟了512字节缓存，用来存放业务对象序列化之后存放的地方，如果空间不足，会自动扩展扩展
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        byte[] bytes;
        try {
            // 序列化
            bytes = ProtostuffIOUtil.toByteArray(new ProtoStuffWrapper<>(t), schema, buffer);
        } finally {
            buffer.clear();
        }
        return bytes;
    }

    /**
     * 反序列化 字节数组=》对象
     *
     * @param bytes 字节数组
     * @return 对象
     * @throws SerializationException 序列化异常
     */
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            ProtoStuffWrapper<T> protoStuffWrapper = new ProtoStuffWrapper<>();
            // 反序列
            ProtostuffIOUtil.mergeFrom(bytes, protoStuffWrapper, schema);
            return protoStuffWrapper.getT();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 序列化包装类，深度克隆，避免无法获取schema
     *
     * @param <T> 业务对象
     */
    public static class ProtoStuffWrapper<T> implements Cloneable {
        private T t;

        ProtoStuffWrapper() {
        }

        ProtoStuffWrapper(T t) {
            this.t = t;
        }

        public T getT() {
            return t;
        }

        public void setT(T t) {
            this.t = t;
        }

        @Override
        @SuppressWarnings("unchecked")
        public ProtoStuffWrapper<T> clone() {
            try {
                return (ProtoStuffWrapper<T>) super.clone();
            } catch (CloneNotSupportedException e) {
                return new ProtoStuffWrapper<T>();
            }
        }
    }
}
